/*
Copyright 2017 yangchong211（github.com/yangchong211）

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package com.yc.video.controller;

import ohos.aafwk.ability.Ability;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.window.service.Window;
import ohos.agp.window.service.WindowManager;
import ohos.app.Context;
import ohos.media.audio.AudioManager;
import ohos.media.audio.AudioRemoteException;
import ohos.multimodalinput.event.TouchEvent;

import com.yc.kernel.utils.VideoLogUtils;
import com.yc.video.config.ConstantKeys;
import com.yc.video.tool.PlayerUtils;
import com.yc.video.tool.TouchHelper;
import com.yc.video.ui.view.InterControlView;

import java.util.Map;

/**
 * 包含手势操作的VideoController
 *
 * @since 2021-05-10
 */
public abstract class GestureVideoController extends BaseVideoController implements Component.ScrolledListener,
    Component.DoubleClickedListener, Component.ClickedListener, Component.TouchEventListener {
    private AudioManager mAudioManager;
    private boolean mIsGestureEnabled = true;
    private int mStreamVolume;
    private float mBrightness;
    private int mSeekPosition;
    /**
     * 是否是第一次触摸
     */
    private boolean mFirstTouch;
    /**
     * 是否改变位置
     */
    private boolean mChangePosition;
    /**
     * 是否改变亮度
     */
    private boolean mChangeBrightness;
    /**
     * 是否改变音量
     */
    private boolean mChangeVolume;
    /**
     * 是否可以改变位置
     */
    private boolean mCanChangePosition = true;
    /**
     * 是否在竖屏模式下开始手势控制
     */
    private boolean mEnableInNormal;
    /**
     * 是否关闭了滑动手势
     */
    private boolean mCanSlide;
    /**
     * 播放状态
     */
    private int mCurPlayState;
    /**
     * 屏幕一半的距离
     */
    private int mHalfScreen;
    /**
     * 用于计算滑动的X
     */
    private float mLastFocusX;
    /**
     * 用于计算滑动的Y
     */
    private float mLastFocusY;

    /**
     * GestureVideoController
     *
     * @param context context
     */
    public GestureVideoController(Context context) {
        super(context);
    }

    /**
     * GestureVideoController
     *
     * @param context context
     * @param attrs attrs
     */
    public GestureVideoController(Context context, AttrSet attrs) {
        super(context, attrs);
    }

    /**
     * GestureVideoController
     *
     * @param context context
     * @param attrs attrs
     * @param defStyleAttr defStyleAttr
     */
    public GestureVideoController(Context context, AttrSet attrs, String defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void initView(Context context) {
        super.initView(context);
        setClickedListener(this);
        mHalfScreen = PlayerUtils.getScreenWidth(getContext(), true) / 2;
        mAudioManager = new AudioManager(context);
        setTouchEventListener(this);
        setDoubleClickedListener(this);
    }

    /**
     * 设置是否可以滑动调节进度，默认可以
     *
     * @param canChangePosition canChangePosition
     */
    public void setCanChangePosition(boolean canChangePosition) {
        mCanChangePosition = canChangePosition;
    }

    /**
     * 是否在竖屏模式下开始手势控制，默认关闭
     *
     * @param enableInNormal enableInNormal
     */
    public void setEnableInNormal(boolean enableInNormal) {
        mEnableInNormal = enableInNormal;
    }

    /**
     * 是否开启手势空控制，默认开启，关闭之后，双击播放暂停以及手势调节进度，音量，亮度功能将关闭
     *
     * @param gestureEnabled
     */
    public void setGestureEnabled(boolean gestureEnabled) {
        mIsGestureEnabled = gestureEnabled;
    }

    /**
     * 调用此方法向控制器设置播放器模式
     *
     * @param playerState 播放模式
     */
    @Override
    public void setPlayerState(int playerState) {
        super.setPlayerState(playerState);
        if (playerState == ConstantKeys.PlayMode.MODE_NORMAL) {
            mCanSlide = mEnableInNormal;
        } else if (playerState == ConstantKeys.PlayMode.MODE_FULL_SCREEN) {
            mCanSlide = true;
        }
    }

    /**
     * 调用此方法向控制器设置播放状态
     *
     * @param playState 播放状态
     */
    @Override
    public void setPlayState(int playState) {
        super.setPlayState(playState);
        mCurPlayState = playState;
    }

    private boolean isInPlaybackState() {
        return mControlWrapper != null
            && mCurPlayState != ConstantKeys.CurrentState.STATE_ERROR
            && mCurPlayState != ConstantKeys.CurrentState.STATE_IDLE
            && mCurPlayState != ConstantKeys.CurrentState.STATE_PREPARING
            && mCurPlayState != ConstantKeys.CurrentState.STATE_PREPARED
            && mCurPlayState != ConstantKeys.CurrentState.STATE_START_ABORT
            && mCurPlayState != ConstantKeys.CurrentState.STATE_BUFFERING_PLAYING;
    }

    /**
     * 单击
     *
     * @param component component
     */
    @Override
    public void onClick(Component component) {
        if (isInPlaybackState()) {
            // 切换显示/隐藏状态
            if (!hideToggleState) { // 是否隐藏进度条
                mControlWrapper.toggleShowState();
            }
        }
    }

    /**
     * 双击
     *
     * @param component component
     */
    @Override
    public void onDoubleClick(Component component) {
        // 如果没有锁屏，
        if (!isLocked() && isInPlaybackState()) {
            // 播放和暂停
            togglePlay();
        }
    }

    /**
     * 在屏幕上滑动
     * 左右滑动，则是改变播放进度
     * 上下滑动，滑动左边改变音量；滑动右边改变亮度
     *
     * @param component component
     * @param i
     * @param i1
     * @param i2
     * @param i3
     */
    @Override
    public void onContentScrolled(Component component, int i, int i1, int i2, int i3) {
    }

    /**
     * slideToChangePosition
     *
     * @param deltaX deltaX
     */
    protected void slideToChangePosition(float deltaX) {
        float temp = -deltaX;
        int width = getEstimatedWidth();
        int duration = (int) mControlWrapper.getDuration();
        int currentPosition = (int) mControlWrapper.getCurrentPosition();
        int position = (int) (temp / width * 120000 + currentPosition);
        if (position > duration) {
            position = duration;
        }
        if (position < 0) {
            position = 0;
        }
        for (Map.Entry<InterControlView, Boolean> next : mControlComponents.entrySet()) {
            InterControlView component = next.getKey();
            if (component instanceof IGestureComponent) {
                ((IGestureComponent) component).onPositionChange(position, currentPosition, duration);
            }
        }
        mSeekPosition = position == 0 ? 1 : position;
    }

    /**
     * slideToChangeBrightness
     *
     * @param deltaY deltaY
     */
    protected void slideToChangeBrightness(float deltaY) {
        Ability activity = PlayerUtils.scanForActivity(getContext());
        if (!PlayerUtils.isActivityLiving(activity)) {
            return;
        }
        Window window = activity.getWindow();
        WindowManager.LayoutConfig attributes = window.getLayoutConfig().get();
        int height = getEstimatedHeight();
        if (mBrightness == -1.0f) {
            mBrightness = 0.5f;
        }
        float brightness = deltaY * 2 / height * 1.0f + mBrightness;
        if (brightness < 0) {
            brightness = 0f;
        }
        if (brightness > 1.0f) {
            brightness = 1.0f;
        }
        int percent = (int) (brightness * 100);
        attributes.windowBrightness = brightness;
        window.setLayoutConfig(attributes);
        for (Map.Entry<InterControlView, Boolean> next : mControlComponents.entrySet()) {
            InterControlView component = next.getKey();
            if (component instanceof IGestureComponent) {
                ((IGestureComponent) component).onBrightnessChange(percent);
            }
        }
    }

    /**
     * slideToChangeVolume
     *
     * @param deltaY deltaY
     */
    protected void slideToChangeVolume(float deltaY) {
        int streamMaxVolume = 0;
        try {
            streamMaxVolume = mAudioManager.getMaxVolume(AudioManager.AudioVolumeType.STREAM_MUSIC);
        } catch (AudioRemoteException e) {
            VideoLogUtils.e(e.getMessage());
        }
        int height = getEstimatedHeight();
        float deltaV = deltaY * 2 / height * streamMaxVolume;
        float index = mStreamVolume + deltaV;
        if (index > streamMaxVolume) {
            index = streamMaxVolume;
        }
        if (index < 0) {
            index = 0;
        }
        int percent = (int) (index / streamMaxVolume * 100);
        mAudioManager.setVolume(AudioManager.AudioVolumeType.STREAM_MUSIC, (int) index);
        for (Map.Entry<InterControlView, Boolean> next : mControlComponents.entrySet()) {
            InterControlView component = next.getKey();
            if (component instanceof IGestureComponent) {
                ((IGestureComponent) component).onVolumeChange(percent);
            }
        }
    }

    private void stopSlide() {
        for (Map.Entry<InterControlView, Boolean> next : mControlComponents.entrySet()) {
            InterControlView component = next.getKey();
            if (component instanceof IGestureComponent) {
                // 结束滑动
                ((IGestureComponent) component).onStopSlide();
            }
        }
    }

    /**
     * 触摸事件
     *
     * @param component component
     * @param touchEvent event事件，主要处理up，down，cancel
     * @return return
     */
    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        VideoLogUtils.e("事件----------事件触摸----------");
        int action = touchEvent.getAction(); // 滑动结束时事件处理
        boolean isHandled = true;
        switch (action) {
            case TouchEvent.PRIMARY_POINT_UP:
                // 抬起View（与DOWN对应）
                stopSlide();
                if (mSeekPosition > 0) {
                    mControlWrapper.seekTo(mSeekPosition);
                    mSeekPosition = 0;
                }
                break;
            case TouchEvent.CANCEL:
                // 非人为原因结束本次事件
                stopSlide();
                mSeekPosition = 0;
                break;
            case TouchEvent.POINT_MOVE:
            case TouchEvent.HOVER_POINTER_MOVE:
                isHandled = onScroll(component, touchEvent);
                break;
            case TouchEvent.PRIMARY_POINT_DOWN:
            case TouchEvent.OTHER_POINT_DOWN:
                // 按下View（所有事件的开始）
                mLastFocusX = TouchHelper.getLocalX(component, touchEvent);
                mLastFocusY = TouchHelper.getLocalY(component, touchEvent);
                isHandled = onDown(touchEvent);
                break;
            default:
                break;
        }
        return isHandled;
    }

    /**
     * 手指按下的瞬间
     *
     * @param event event
     * @return return
     */
    public boolean onDown(TouchEvent event) {
        if (!isInPlaybackState()
            || !mIsGestureEnabled
            || PlayerUtils.isEdge(getContext(), event)) {
            // 关闭了手势
            // 不处于播放状态
            // 处于屏幕边沿
            return true;
        }
        try {
            mStreamVolume = mAudioManager.getVolume(AudioManager.AudioVolumeType.STREAM_MUSIC);
        } catch (AudioRemoteException audioRemoteException) {
            VideoLogUtils.e(audioRemoteException.getMessage());
        }
        Ability activity = PlayerUtils.scanForActivity(getContext());
        if (activity == null) {
            mBrightness = 0;
        } else {
            mBrightness = activity.getWindow().getLayoutConfig().get().windowBrightness;
        }
        mFirstTouch = true;
        mChangePosition = false;
        mChangeBrightness = false;
        mChangeVolume = false;
        return true;
    }

    /**
     * 是否控制菜单进度条拖动（解决滑动进度窗口冲突）
     *
     * @return return
     */
    protected boolean isDragging() {
        return false;
    }

    /**
     * onScroll
     *
     * @param component component
     * @param event event
     * @return return
     */
    public boolean onScroll(Component component, TouchEvent event) {
        if (!isInPlaybackState()
            || !mIsGestureEnabled
            || !mCanSlide
            || isLocked()
            || isDragging()
            || PlayerUtils.isEdge(getContext(), event)) {
            // 不处于播放状态
            // 关闭了手势
            // 关闭了滑动手势
            // 锁住了屏幕
            // 控制菜单进度条正在拖动
            // 处于屏幕边沿
            return false;
        }

        int deltaX = (int) (mLastFocusX - TouchHelper.getLocalX(component, event));
        int deltaY = (int) (mLastFocusY - TouchHelper.getLocalY(component, event));
        int distanceX = Math.abs(deltaX);
        int distanceY = Math.abs(deltaY);
        if (10 > distanceX && 10 > distanceY) {
            return true;
        }
        if (mFirstTouch) { // 如果是第一次触摸
            // 判断是左右滑动，还是上下滑动
            mChangePosition = distanceX >= distanceY;
            if (!mChangePosition) {
                // 上下滑动，滑动左边改变音量；滑动右边改变亮度
                // 半屏宽度
                if (mHalfScreen == 0) {
                    mHalfScreen = PlayerUtils.getScreenWidth(getContext(), true) / 2;
                }
                if (event.getPointerScreenPosition(0).getX() > mHalfScreen) {
                    mChangeVolume = true;
                } else {
                    mChangeBrightness = true;
                }
            }
            if (mChangePosition) { // 左右滑动，则是改变播放进度
                // 根据用户设置是否可以滑动调节进度来决定最终是否可以滑动调节进度
                mChangePosition = mCanChangePosition;
            }
            if (mChangePosition || mChangeBrightness || mChangeVolume) {
                for (Map.Entry<InterControlView, Boolean> next : mControlComponents.entrySet()) {
                    InterControlView controlView = next.getKey();
                    if (controlView instanceof IGestureComponent) {
                        ((IGestureComponent) controlView).onStartSlide();
                    }
                }
            }
            mFirstTouch = false;
        }
        if (mChangePosition) {
            slideToChangePosition(deltaX);
        } else if (mChangeBrightness) {
            slideToChangeBrightness(deltaY);
        } else if (mChangeVolume) {
            slideToChangeVolume(deltaY);
        }
        return true;
    }
}
